﻿using System.Collections.Generic;
using UnityEngine;

///////////////////////////////////////////////////////////
//
//  Written by              ：laishikai
//  Copyright(C)            ：成都博瑞梦工厂
//  ------------------------------------------------------
//  功能描述                ：通用的图集管理器
//
///////////////////////////////////////////////////////////

// 单个纹理材质
public class UITexAtlas
{
    public enum Coordinates
    {
        Pixels,
        TexCoords,
    }

    public string m_szAtlasName = "Input name"; // 材质名字
    public string m_szTexName = "";   // 纹理名字
    public string m_szShaderName = ""; // shader名字
    public Material m_material;    // 材质
    public Texture m_MainAlpha;    // 主贴图的通道图
    public int m_nAtlasID = 0;        // 材质ID

    Coordinates m_Coordinates = Coordinates.Pixels;

    // Size in pixels for the sake of MakePixelPerfect functions.
    int m_PixelSize = 1;

    int m_nTexWidth = 1;   // 纹理的宽度
    int m_nTexHeight = 1;  // 纹理的高度

    bool m_bCanLOD = false; // 是不是可以LOD缩放
    
    // -----------------------------------------------------------
    // 以下时临时变量
    public int m_nRef;  // 引用计数
    // -----------------------------------------------------------

    public Texture MainAlphaTexture
    {
        get { return m_MainAlpha != null ? m_MainAlpha : mainTexture; }
    }

    public Texture mainTexture
    {
        get { if (m_material != null) return m_material.mainTexture; else return null; }
    }
    public int texWidth
    {
        get{ return m_nTexWidth; }
    }
    public int texHeight
    {
        get { return m_nTexHeight; }
    }
    
    public Coordinates coordinates
    {
        get
        {
            return m_Coordinates;
        }
        set
        {
            m_Coordinates = value;
        }
    }

    public void Serailize(ref CSerialize ar)
    {
        int nCoordinatesType = (int)m_Coordinates;
        ar.ReadWriteValue(ref m_szAtlasName);
        ar.ReadWriteValue(ref m_szTexName);
        ar.ReadWriteValue(ref nCoordinatesType);
        ar.ReadWriteValue(ref m_PixelSize);
        m_Coordinates = nCoordinatesType == (int)Coordinates.Pixels ? Coordinates.Pixels : Coordinates.TexCoords;
        ar.ReadWriteValue(ref m_nTexWidth);
        ar.ReadWriteValue(ref m_nTexHeight);
        if (ar.GetVersion() >= 1)
        {
            ar.ReadWriteValue(ref m_nAtlasID);
        }
        if (ar.GetVersion() >= 2)
        {
            ar.ReadWriteValue(ref m_szShaderName);
        }
        if(ar.GetVersion() >= 4)
        {
            ar.ReadWriteValue(ref m_bCanLOD);
        }
    }
    public void SerializeToTxt(ref SerializeText ar)
    {
        int nCoordinatesType = (int)m_Coordinates;
        ar.ReadWriteValue("AtlasName", ref m_szAtlasName);
        ar.ReadWriteValue("TexName", ref m_szTexName);
        ar.ReadWriteValue("Coordinates", ref nCoordinatesType);
        ar.ReadWriteValue("PixelSize", ref m_PixelSize);
        m_Coordinates = nCoordinatesType == (int)Coordinates.Pixels ? Coordinates.Pixels : Coordinates.TexCoords;
        ar.ReadWriteValue("texWidth", ref m_nTexWidth);
        ar.ReadWriteValue("texHeight", ref m_nTexHeight);
        if (ar.GetVersion() >= 1)
        {
            ar.ReadWriteValue("AtlasID", ref m_nAtlasID);
        }
        if (ar.GetVersion() >= 2)
        {
            ar.ReadWriteValue("ShaderName", ref m_szShaderName);
        }
        if(ar.GetVersion() >= 4)
        {
            ar.ReadWriteValue("CanScale", ref m_bCanLOD);
        }
    }
};

public class UISpriteInfo  // 兼容NGUI的Sprite对象，将Sprite成员放到这里来
{
    public string name = "Unity Bug";   // 对象的名字
    public Rect outer = new Rect(0f, 0f, 1f, 1f);     // 外框，精灵的实际大小（在纹理的像素坐标)
	public Rect inner = new Rect(0f, 0f, 1f, 1f);     // 内框，用来做填充模式时的像素坐标，这个必须是在外框之内的
    public bool rotated = false;

    // Padding is needed for trimmed sprites and is relative to sprite width and height
    public float paddingLeft = 0f;   // 用来做精灵图层选择时扩展选择框范围的东东，没有实际意义
    public float paddingRight = 0f;
    public float paddingTop = 0f;
    public float paddingBottom = 0f;

    // 下面是扩展属性
    public int m_nNameID;   // 精灵ID
    public int m_nAtlasID;  // 材质ID
    public string m_szAtlasName;  // 对应的材质名字

    public void Serailize(ref CSerialize ar)
    {
        ar.ReadWriteValue(ref name);
        ar.ReadWriteValue(ref outer);
        ar.ReadWriteValue(ref inner);
        ar.ReadWriteValue(ref rotated);
        ar.ReadWriteValue(ref paddingLeft);
        ar.ReadWriteValue(ref paddingRight);
        ar.ReadWriteValue(ref paddingTop);
        ar.ReadWriteValue(ref paddingBottom);
        ar.ReadWriteValue(ref m_szAtlasName);
        if (ar.GetVersion() >= 1)
        {
            ar.ReadWriteValue(ref m_nNameID);
            ar.ReadWriteValue(ref m_nAtlasID);
        }
    }
    public void SerializeToTxt(ref SerializeText ar)
    {
        ar.ReadWriteValue("name", ref name);
        ar.ReadWriteValue("outer", ref outer);
        ar.ReadWriteValue("inner", ref inner);
        ar.ReadWriteValue("rotated", ref rotated);
        ar.ReadWriteValue("paddingLeft", ref paddingLeft);
        ar.ReadWriteValue("paddingRight", ref paddingRight);
        ar.ReadWriteValue("paddingTop", ref paddingTop);
        ar.ReadWriteValue("paddingBottom", ref paddingBottom);
        ar.ReadWriteValue("AtlasName", ref m_szAtlasName);
        if (ar.GetVersion() >= 1)
        {
            ar.ReadWriteValue("NameID", ref m_nNameID);
            ar.ReadWriteValue("AtlasID", ref m_nAtlasID);
        }
    }
}

// 材质管理器
public class CAtlasMng
{
    public class CUITextureCache
    {
        public Texture m_tex;
        public Texture m_defTex;
        public float m_fTime = 0.0f;
        public int m_nRef = 0;
        public bool m_bFromAssets = false;
        public bool m_bQueryAssetBundleRef = false;
        public string m_szResName = string.Empty;
        public AssetBundle m_mainBundle;
    };

    protected Dictionary<string, UISpriteInfo> m_AllSprite = new Dictionary<string, UISpriteInfo>();   // 所有的精灵对象
    protected Dictionary<string, UITexAtlas> m_TexAtlas = new Dictionary<string, UITexAtlas>();    // 材质对象
    protected Dictionary<int, UITexAtlas> m_QueryAtlas = new Dictionary<int, UITexAtlas>();
    protected CMyArray<UISpriteInfo> m_SpritePtr = new CMyArray<UISpriteInfo>();
    protected CMyArray<UITexAtlas> m_AtlasPtr = new CMyArray<UITexAtlas>();
    protected CMyArray<int> m_NeedReleaseAtlas = new CMyArray<int>();
    
    protected Dictionary<string, CUITextureCache> m_TexCache = new Dictionary<string, CUITextureCache>();    // 外部缓冲纹理对象
    protected CMyArray<string> m_tempDelete = new CMyArray<string>();

    protected UISpriteInfo m_defSprite = new UISpriteInfo();
    protected bool m_bInitCfg = false;
    protected int n_nObjNumb;
    protected float m_fNextUpdateTime = 0.0f;
    protected int m_nFileVersion = 0;  // 文件的版本号，每次保存加1 

    protected bool m_bLoadingCfg = false;

    public delegate void LPFuncAddTextureRef(string szResName, int nAddRef, bool bImmRelease);

    public LPFuncAddTextureRef m_lpFuncAddTextureRef = null;    // 增加纹理资源引用计数

    static CAtlasMng s_pAltasMng;
    static int s_nLowQuality = 0; // 是不是使用低画质

    // 功能：是不是低画质
    static public bool IsLowQuality()
    {
        if (0 == s_nLowQuality)
            s_nLowQuality = SystemInfo.systemMemorySize <= 1024 ? 1 : 2; // 如果内存小于1G，就认定是低画质
        return s_nLowQuality == 1;
    }

    public class CAtlasMngStart : MonoBehaviour
    {
        CAtlasMng m_pMng;
        public CAtlasMngStart()
        {
            if (s_pAltasMng == null)
            {
                m_pMng = s_pAltasMng = new CAtlasMng();
            }
        }
        void Start()
        {
            // 释放自己吧
            if (m_pMng == null)
            {
                if (gameObject != null)
                    DestroyImmediate(gameObject);
                else
                    DestroyImmediate(this);
            }
            else
            {
                m_pMng.AutoInit();
            }
        }
        void FixedUpdate()
        {
            if (m_pMng != null)
            {
                m_pMng.ReleaseLogic();
            }
        }
    };

    static public CAtlasMng instance
    {
        get
        {
            if (s_pAltasMng == null)
            {
                CAtlasMngStart pStart = Object.FindObjectOfType(typeof(CAtlasMngStart)) as CAtlasMngStart;
                if (pStart == null)
                {
                    GameObject go = GameObject.Find("_AltasMng_Start");
                    if (go != null)
                    {
                        GameObject.Destroy(go);
                        go = null;
                    }
                    if (go == null)
                    {
                        go = new GameObject("_AltasMng_Start");
                        if (Application.isPlaying)
                            MonoBehaviour.DontDestroyOnLoad(go);
                    }
                    go.hideFlags = HideFlags.HideAndDontSave;
                    pStart = go.GetComponent<CAtlasMngStart>();
                    if (pStart == null)
                        pStart = go.AddComponent<CAtlasMngStart>();
                }
            }
            if (s_pAltasMng != null)
                s_pAltasMng.AutoInit();
            return s_pAltasMng;
        }
    }

    public void FirstLod()
    {
        if (m_bLoadingCfg)
            return;
        m_bLoadingCfg = true;

        TextAsset text = HUDMgr.Instance.HUDSetting.AssetAll;
        this.InitCfg(text.bytes);
    }

    public void ReleaseLogic()
    {
        float  fNow = Time.time;
        if (fNow > m_fNextUpdateTime)
        {
            m_fNextUpdateTime = fNow + 5.0f; // 每10秒走一下释放逻辑
            bool bRemove = false;
            bool bLowQuality = IsLowQuality();
            for (int i = m_NeedReleaseAtlas.size() - 1; i >= 0; --i )
            {
                UITexAtlas atlas = GetAtlasByID(m_NeedReleaseAtlas[i]);
                bRemove = false;
                if (atlas.m_nRef > 0)
                {
                    bRemove = true;
                }
                if (bRemove)
                    m_NeedReleaseAtlas.pop_back();
                else
                    break;
            }
            if (m_TexCache.Count > 0)
            {           
                ReleaseTextureCache(false);
            }
            if(bLowQuality && (m_NeedReleaseAtlas.size() + m_TexCache.Count) > 5)
            {
                m_fNextUpdateTime = fNow + 0.1f;
            }
        }
    }
    void DestroyTexture(Texture tex, bool bFromAssets)
    {
        if (tex != null)
        {
            if (bFromAssets)
                Object.DestroyImmediate(tex, true);
            else
                Object.Destroy(tex);
        }
    }
    void ReleaseTextureCache(bool bSwitchScene)
    {
        float fNow = Time.time;
        bool bLowQuality = IsLowQuality();
        Dictionary<string, CUITextureCache>.Enumerator itCache = m_TexCache.GetEnumerator();
        while (itCache.MoveNext())
        {
            CUITextureCache pTexCache = itCache.Current.Value;
            if (pTexCache.m_nRef == 0)
            {
                if ( bSwitchScene 
                    || pTexCache.m_fTime + 15.0f < fNow 
                    || (bLowQuality && m_TexCache.Count > 2))
                {
                    ImmReleaseCacheTexture(pTexCache);
                    m_tempDelete.push_back(itCache.Current.Key);
                }
            }
        }
        if (m_tempDelete.size() > 0)
        {
            for (int i = 0, iCount = m_tempDelete.size(); i < iCount; ++i)
            {
                m_TexCache.Remove(m_tempDelete[i]);
            }
            m_tempDelete.Clear();
        }
    }
    void  ImmReleaseCacheTexture(CUITextureCache pTexCache)
    {
        if(pTexCache.m_bQueryAssetBundleRef)
        {
            pTexCache.m_bQueryAssetBundleRef = false;
            m_lpFuncAddTextureRef(pTexCache.m_szResName, -1, true);
            pTexCache.m_tex = null;
        }

        if (pTexCache.m_tex != pTexCache.m_defTex)
            DestroyTexture(pTexCache.m_tex, pTexCache.m_bFromAssets);
        DestroyTexture(pTexCache.m_defTex, false);
        if (pTexCache.m_mainBundle != null)
        {
            pTexCache.m_mainBundle.Unload(true);
            pTexCache.m_mainBundle = null;
        }
        pTexCache.m_defTex = null;
        pTexCache.m_tex = null;
    }

    void SerializeIterator(CSerialize ar, ref string key, ref UISpriteInfo value)
    {
        if (key == null) key = string.Empty;
        if (value == null) value = new UISpriteInfo();
        ar.ReadWriteValue(ref key);
        value.Serailize(ref ar);
    }
    void SerializeIterator(CSerialize ar, ref string key, ref UITexAtlas value)
    {
        if (key == null) key = string.Empty;
        if (value == null) value = new UITexAtlas();
        ar.ReadWriteValue(ref key);
        value.Serailize(ref ar);
    }
    void SerializeIterator(SerializeText ar, ref string key, ref UISpriteInfo value)
    {
        if (key == null) key = string.Empty;
        if (value == null) value = new UISpriteInfo();
        ar.ReadWriteValue("Sprite", ref key);
        value.SerializeToTxt(ref ar);
    }
    void SerializeIterator(SerializeText ar, ref string key, ref UITexAtlas value)
    {
        if (key == null) key = string.Empty;
        if (value == null) value = new UITexAtlas();
        ar.ReadWriteValue("Atlas", ref key);
        value.SerializeToTxt(ref ar);
    }
    protected void Serialize(CSerialize ar)
    {
        byte yVersion = 4;
        ar.ReadWriteValue(ref yVersion);
        ar.SetVersion(yVersion);
        if (yVersion > 2)
            ar.ReadWriteValue(ref m_nFileVersion);
        ar.SerializeDictionary<string, UISpriteInfo>(ref m_AllSprite, SerializeIterator);
        ar.SerializeDictionary<string, UITexAtlas>(ref m_TexAtlas, SerializeIterator);
    }
    protected void SerializeToTxt(SerializeText ar)
    {
        byte yVersion = 4;
        ar.ReadWriteValue("Version", ref yVersion);
        if (yVersion > 2)
            ar.ReadWriteValue("SaveCount",ref m_nFileVersion);
        ar.SetVersion(yVersion);
        ar.SerializeDictionary<string, UISpriteInfo>("AllSprite", ref m_AllSprite, SerializeIterator);
        ar.SerializeDictionary<string, UITexAtlas>("AllAtlas", ref m_TexAtlas, SerializeIterator);
    }   

    public void AutoInit()
    {
        if (!m_bInitCfg || n_nObjNumb != (m_AllSprite.Count + m_TexAtlas.Count + 10) )
        {
            m_bInitCfg = true;
            InitAltasCfg();
        }
    }

    protected bool MakeSpriteAtlasID()
    {
        m_SpritePtr.Clear();
        m_AtlasPtr.Clear();
        m_SpritePtr.reserve(m_AllSprite.Count);
        m_AtlasPtr.reserve(m_TexAtlas.Count);

        bool bDirty = false;
        CMyArray<UITexAtlas> newAtlas = new CMyArray<UITexAtlas>();

        Dictionary<string, UITexAtlas>.Enumerator itAtlas = m_TexAtlas.GetEnumerator();
        int nMaxAtlasID = m_TexAtlas.Count + 1;
        while (itAtlas.MoveNext())
        {
            UITexAtlas atlas = itAtlas.Current.Value;
            if (atlas.m_nAtlasID > 0 && atlas.m_nAtlasID <= nMaxAtlasID)
            {
                if (m_AtlasPtr.IsValid(atlas.m_nAtlasID - 1) && m_AtlasPtr[atlas.m_nAtlasID - 1] != null)
                {
                    newAtlas.push_back(atlas);
                }
                else
                {
                    m_AtlasPtr.GrowSet(atlas.m_nAtlasID - 1, atlas);
                }
            }
            else
            {
                newAtlas.push_back(atlas);
            }
        }
        if (newAtlas.size() > 0)
            bDirty = true;
        int nStartPos = m_AtlasPtr.FindNextNull(0);
        for (int i = 0; i < newAtlas.size(); ++i)
        {
            UITexAtlas atlas = newAtlas[i];
            atlas.m_nAtlasID = m_AtlasPtr.FindNextNull(nStartPos) + 1;
            nStartPos = atlas.m_nAtlasID;
            m_AtlasPtr.GrowSet(atlas.m_nAtlasID - 1, atlas);
        }

        CMyArray<UISpriteInfo> newSprite = new CMyArray<UISpriteInfo>();

        Dictionary<string, UISpriteInfo>.Enumerator itSprite = m_AllSprite.GetEnumerator();
        int nMaxID = m_AllSprite.Count + 1;
        while (itSprite.MoveNext())
        {
            UISpriteInfo sp = itSprite.Current.Value;
            sp.m_nAtlasID = AtlasNameToID(sp.m_szAtlasName);
            if (sp.m_nNameID > 0 && sp.m_nNameID <= nMaxID)
            {
                if (m_SpritePtr.IsValid(sp.m_nNameID - 1) && m_SpritePtr[sp.m_nNameID - 1] != null)
                {
                    // 重复的ID
                    newSprite.push_back(sp);
                }
                else
                {
                    m_SpritePtr.GrowSet(sp.m_nNameID - 1, sp);
                }
            }
            else
            {
                newSprite.push_back(sp);
            }
        }
        nStartPos = m_SpritePtr.FindNextNull(0);
        int nNewSpriteCount = newSprite.size();
        for (int i = 0; i < nNewSpriteCount; ++i)
        {
            UISpriteInfo sp = newSprite[i];
            sp.m_nNameID = m_SpritePtr.FindNextNull(nStartPos) + 1;
            nStartPos = sp.m_nNameID;
            m_SpritePtr.GrowSet(sp.m_nNameID - 1, sp);
        }
        if (newSprite.size() > 0)
            bDirty = true;
        return bDirty;
    }

    public void InitCfg(byte[] fileData)
    {
        if (fileData != null && m_TexAtlas.Count == 0)
        {
            m_bInitCfg = true;

            //mac 改
            SerializeText ar = new SerializeText(SerializeType.read, fileData, fileData.Length);
            SerializeToTxt(ar);

            n_nObjNumb = m_AllSprite.Count + m_TexAtlas.Count + 10;
            MakeSpriteAtlasID();
            // 必要的话，通知所有界面
        }
        m_bLoadingCfg = false;
    }

    // 功能：初始化配置吧
    public virtual void InitAltasCfg()
    {
        m_bInitCfg = true;
        n_nObjNumb = m_AllSprite.Count + m_TexAtlas.Count + 10;

        // 异步加载吧
        FirstLod();
    }

    // 加载吧
    protected virtual bool QueryAltasTex(UITexAtlas atlas)
    {
        Texture tex = HUDMgr.Instance.HUDSetting.GetTextureByPath("Atlases/" + atlas.m_szAtlasName);
        if (atlas.m_material == null)
        {
            Shader shader = HUDMgr.Instance.HUDSetting.GetShaderByPath("Shaders/Unlit-TransparentColored");
            atlas.m_material = new Material(shader);
        }
        atlas.m_material.mainTexture = tex;
        return true;
    }

    public UITexAtlas GetAtlasByID(int nAtlasID)
    {
        if (m_AtlasPtr.IsValid(nAtlasID - 1))
            return m_AtlasPtr[nAtlasID - 1];
        return null;
    }
    public int AtlasNameToID(string szAtlasName)
    {
        if (string.IsNullOrEmpty(szAtlasName))
            return 0;
        UITexAtlas atlas = null;
        if (m_TexAtlas.TryGetValue(szAtlasName, out atlas))
        {
            return atlas.m_nAtlasID;
        }
        return 0;
    }
    public int SpriteNameToID(string szSpriteName)
    {
        if (string.IsNullOrEmpty(szSpriteName))
            return 0;
        UISpriteInfo sprite = null;
        if (m_AllSprite.TryGetValue(szSpriteName, out sprite))
        {
            return sprite.m_nNameID;
        }
        return 0;
    }

    // 功能：申请图集的资源
    public void QueryAtlasByID(int nAtlasID)
    {
        UITexAtlas atlas = GetAtlasByID(nAtlasID);
        QueryByAtlas(atlas);
    }
    private void QueryByAtlas(UITexAtlas atlas)
    {
        if (atlas != null)
        {
            if (atlas.m_material == null || atlas.m_material.mainTexture == null)
                QueryAltasTex(atlas);
            atlas.m_nRef++;
            m_QueryAtlas[atlas.m_nAtlasID] = atlas;
        }
    }
    void PushReleaseAtlasID(int nAtlasID)
    {
        for (int i = 0, iLen = m_NeedReleaseAtlas.size(); i < iLen; ++i)
        {
            if (m_NeedReleaseAtlas[i] == nAtlasID)
                return;
        }
        m_NeedReleaseAtlas.push_front(nAtlasID);
    }
    // 功能：释放图集
    public void ReleaseAtlasByID(int nAtlasID)
    {
        UITexAtlas atlas = GetAtlasByID(nAtlasID);
        ReleaseByAtlas(atlas);
    }
    private  void ReleaseByAtlas(UITexAtlas atlas)
    {
        if (atlas != null)
        {
            atlas.m_nRef--;
            if (atlas.m_nRef == 0)
            {
                // 从前面添加
                PushReleaseAtlasID(atlas.m_nAtlasID);

                m_QueryAtlas.Remove(atlas.m_nAtlasID);

                if(IsLowQuality() && m_NeedReleaseAtlas.size() > 5)
                {
                    m_fNextUpdateTime = 0.0f;
                }
            }
        }
    }
    public UISpriteInfo GetSpriteByID(int nSpriteID)
    {
        if (m_SpritePtr.IsValid(nSpriteID - 1))
            return m_SpritePtr[nSpriteID - 1];
        return null;
    }
    public UISpriteInfo GetSafeSpriteByID(int nSpriteID)
    {
        UISpriteInfo sprite = GetSpriteByID(nSpriteID);
        if (sprite != null)
            return sprite;
        return m_defSprite;
    }
}
